|
アプリケーションを正しく動作させるには、辞書、プラグイン、スクリプトなどの追加ファイルが必要になります。Apple ではそのようなファイルを、「アプリケーションサポート」、「スクリプティング機能追加」、「スクリプト」、「Internet プラグイン」などのフォルダに格納することを推奨します。これらのフォルダは、Mac OS 8、9、および Mac OS X では透過的に動作する FindFolder API を使ってその位置を確認することができますが、これらのファイルを、関連のあるアプリケーションに隣接するフォルダに格納することを好むデベロッパもいます。そのように格納されているファイルを探すには、先にアプリケーションが自身の格納されている場所を知る必要があります。Mac OS X では、これらの作業をするのに過去にデベロッパが利用してきたさまざまな方法を見直す必要があります。
Mac OS X 上で アプリケーションが Classic アプリケーションとして動作している場合には、アプリケーションの位置を把握するのに現在使用されている方法はそのまま機能します。これが Classic アプリケーションによって実現される互換性です。このテクニカルノートをこれ以上読む必要はありません。
アプリケーションを Mac OS X 上で独立のモダンプロセスとして動作させることによって得られる数々の利点を得るためアプリケーションを Carbon 対応にしている場合には、このテクニカルノートを読む必要があります。
[2001年3月27日]
|
パッケージの使用
アプリケーションをすでに Carbon 対応にしていてもパッケージとして提供していないなら、そうすることを強く推奨します。パッケージは Mac OS 9.0 以降でサポートされており、ソフトウェアを配布する方法として推奨されています。MetroWerks CodeWarrior によって生成されているものと同じ、CFM ベースのデータフォークとリソースフォークを持つ単一のファイルとしてアプリケーションを構築している段階にあるとしても、Mac OS 9.x と Mac OS X の両方で動作する最小限のパッケージとしてアプリケーションを提供することができます。
folder MyCFMPkgAppl.app
alias MyCFMPkgAppl alias
folder Contents
folder MacOSClassic
appl. MyCFMPkgAppl
|
このパッケージ構造を使用すれば、CFBundle API を使ってパッケージやその構成要素の位置を探せるという利点があります。また、最終的に Mach-O コード形式に移行するときにもコードの互換性が確保されている、という利点があります。さらに、Apple の ProjectBuilder によってパッケージ構造でのみ配布される Mac OS X 上で動作することの利点をすべて享受できます。
folder MyMachOPkgAppl.app
alias MyMachOPkgAppl alias
folder Contents
folder MacOS
appl. MyMachOPkgAppl
folder Resources
file MyMachOPkgAppl.rsrc
… …
… …
|
今パッケージ構造を使用しなければ、将来同じ Carbon API を使うとしても、CFM から Mach-O に切り換えるときに再度コードを変えなければなりません。
CFBundle API
実行するアプリケーションの位置を特定するのにデベロッパがよく使用している方法が3つあります。それらに代わる同等のモダン CFBundle API が3つあり、さらに Mac OS X のみで利用可能な Process Manager API が1つあります。
以下に示すのが3つの CFBundle API です。
OSErr GetApplicationPackageFSSpecFromBundle( FSSpecPtr theFSSpecPtr )
{
OSErr err = fnfErr;
CFBundleRef myAppsBundle = CFBundleGetMainBundle();
if( myAppsBundle == NULL )
return err;
CFURLRef myBundleURL = CFBundleCopyBundleURL( myAppsBundle );
if( myBundleURL == NULL )
return err;
FSRef myBundleRef;
Boolean ok = CFURLGetFSRef( myBundleURL, &myBundleRef );
CFRelease( myBundleURL );
if( !ok )
return err;
return FSGetCatalogInfo( &myBundleRef, kFSCatInfoNone,
NULL, NULL, theFSSpecPtr, NULL);
}
OSErr GetApplicationResourceFSSpecFromBundle( FSSpecPtr theFSSpecPtr )
{
OSErr err = fnfErr;
CFBundleRef myAppsBundle = CFBundleGetMainBundle();
if( myAppsBundle == NULL )
return err;
CFURLRef myBundleURL = CFBundleCopyResourcesDirectoryURL( myAppsBundle );
if( myBundleURL == NULL )
return err;
FSRef myBundleRef;
Boolean ok = CFURLGetFSRef( myBundleURL, &myBundleRef );
CFRelease( myBundleURL );
if( !ok )
return err;
return FSGetCatalogInfo( &myBundleRef, kFSCatInfoNone,
NULL, NULL, theFSSpecPtr, NULL);
}
OSErr GetExecutableParentFSSpecFromBundle( FSSpecPtr theFSSpecPtr )
{
OSErr err = fnfErr;
CFBundleRef myAppsBundle = CFBundleGetMainBundle();
if( myAppsBundle == NULL )
return err;
CFURLRef myBundleURL = CFBundleCopyExecutableURL( myAppsBundle );
if( myBundleURL == NULL )
return err;
FSRef myBundleRef;
Boolean ok = CFURLGetFSRef( myBundleURL, &myBundleRef );
CFRelease( myBundleURL );
if( !ok )
return err;
return FSGetCatalogInfo( &myBundleRef, kFSCatInfoNone,
NULL, NULL, theFSSpecPtr, NULL);
}
|
リスト1 - CFBundle API
|
Mac OS X のみで利用可能な Process Manager の追加 API
OSErr GetApplicationBundleFSSpec( FSSpecPtr theFSSpecPtr )
{
OSErr err;
ProcessSerialNumber psn;
err = GetCurrentProcess(&psn);
if( err != noErr )
return err;
FSRef location;
err = GetProcessBundleLocation(&psn, &location);
if( err != noErr )
return err;
return FSGetCatalogInfo( &location, kFSCatInfoNone,
NULL, NULL, theFSSpecPtr, NULL);
}
|
リスト2 - Process Manager API
|
従来の方法
下記はこれまでデベロッパが使っていた3つの従来の方法です。
OSErr GetApplicationPackageFSSpec( FSSpecPtr theFSSpecPtr )
{
OSErr err;
Str255 applName;
ProcessSerialNumber psn;
ProcessInfoRec info;
info.processInfoLength = sizeof(ProcessInfoRec);
info.processName = applName;
info.processAppSpec = theFSSpecPtr;
err = GetCurrentProcess( &psn );
if( err != noErr )
return err;
err = GetProcessInformation( &psn, &info );
return err;
}
OSErr GetApplicationResourceFSSpec( OSType creator,
FSSpecPtr theFSSpecPtr)
{
Handle creatorHandle;
OSErr err;
Str255 applName;
FCBPBRec theFCBPBRec;
// creator は必ず一意であり、また必ず
// アプリケーションのリソースフォーク
// のみに対応する、4バイトのクリエータコードである
creatorHandle = GetResource( creator, 0 );
if( creatorHandle == NULL )
return resNotFound;
theFCBPBRec.ioCompletion = nil;
theFCBPBRec.ioNamePtr = applName;
theFCBPBRec.ioRefNum = HomeResFile( creatorHandle );
theFCBPBRec.ioFCBIndx = 0;
err = PBGetFCBInfoSync( &theFCBPBRec );
if( err != noErr )
return err;
err = FSMakeFSSpec( theFCBPBRec.ioFCBVRefNum,
theFCBPBRec.ioFCBParID, applName, theFSSpecPtr );
return err;
}
OSErr GetExecutableParentFSSpec( OSType creator, FSSpecPtr theFSSpecPtr )
{
Handle creatorHandle;
OSErr err;
Str255 volName;
short vRefNum;
long dirID;
// creator は必ず一意であり、また必ず
// アプリケーションリソースフォーク
// のみに対応する、4バイトのクリエータコードである
creatorHandle = GetResource( creator, 0 );
if( creatorHandle == NULL )
return resNotFound;
err = HGetVol( volName, &vRefNum, &dirID );
if( err != noErr )
return err;
// HGetVol によって返される vRefNum は実際は
// ボリュームの Reference Number ではない。
// そのため、実行可能なものとリソースが
// 同じボリューム上にあると想定する、以下の
// 追加コールが必要になる。
err = GetVRefNum( HomeResFile( creatorHandle ), &vRefNum );
if( err != noErr )
return err;
err = FSMakeFSSpec( vRefNum, dirID, nil, theFSSpecPtr );
return err;
}
|
リスト3 - 従来の方法
|
文書化されていない File Manager ルーチンの副作用を使った(つまり、信頼性がないということ)4つ目の方法についても念のため記しておきましょう。アプリケーションを起動してすぐに FSMakerFSSpec(0,0,nil,theFSSpecPtr)を呼び出すと FSSpec はデフォルトのボリュームのデフォルトのディレクトリの、そしてアプリケーションが格納されているフォルダで埋められています。
結果
パッケージ化と CFBundle API の利点を示すために、以下のすべてのケースにおいてそれぞれの方法が何を返すかを見てみましょう。
ケース 1: Mac OS 9 上で実行されるファイルとして配布される Carbon CFM アプリケーション
ケース 2: Mac OS X 上で実行されるファイルとして配布される Carbon CFM アプリケーション
ケース 3: Mac OS 9 上で実行されるパッケージとして配布される Carbon CFM アプリケーション
ケース 4: Mac OS X 上で実行されるパッケージとして配布される Carbon CFM アプリケーション
最終的な目標:
ケース 5: Mac OS X 上で実行されるパッケージとして配布される Carbon Mach-O アプリケーション
ここで CFBundle による方法 1.a、1.b 、1.c、Process Manager による方法 2、そして従来の方法 3a、3b、3cを呼び出します。
FSSpec が指す対象を以下の表に示します。
| 1.a | 1.b | 1.c | 2 | 3.a | 3.b | 3.c |
ケース 1 | アプリケーションフォルダ | N/A | アプリケーションファイル | N/A | アプリケーションファイル | アプリケーションファイル | アプリケーションフォルダ
|
ケース 2 | アプリケーションフォルダ | アプリケーションフォルダ | アプリケーションファイル | N/A | アプリケーションファイル | アプリケーションファイル | アプリケーションフォルダ |
ケース 3 | .app「フォルダ」 | N/A | アプリケーションバイナリ | N/A | アプリケーションバイナリ | アプリケーションバイナリ | MacOSClassic |
ケース 4 | .app「フォルダ」 | N/A | アプリケーションバイナリ | N/A | アプリケーションバイナリ | アプリケーションバイナリ | MacOSClassic |
ケース 5 | .app「folder」 | Resources | アプリケーションバイナリ | .app「フォルダ」 | アプリケーションバイナリ | rsrc ファイル | MacOS
|
アプリケーションがパッケージとして配布された場合には、アプリケーションバイナリ(対象コードを含むファイル)の位置を探してもあまり意味がありません。というのも、パッケージの構造が決して変わらないと考えるのは危険だからです。このため、アプリケーションをユーザーの見たままの(アイコン、つまり.app「フォルダ」のように)位置で探す唯一の安全な方法は、方法の1.aあるいは2です。
現在の配布上の制約によっては(例えばターゲットとしているプラットフォームがどれかによっては)どの方法が最適かを判断しなければなりませんが、上記の表を見ればわかるように、ケース5 へのルートを作るには、まだ CFM フォーマットを一時的に使用していたとしても、パッケージ構造を採用して、CFBundle API を使うのが最良の方法です。
先頭に戻る
参考文献
テクニカルノートTN1188「Packages in Mac OS 9」
先頭に戻る
|